#include "tcpconn.h"
#include <iostream>

const char* doc_root = "/root/MyShare/resources/";
connection_pool* tcpconn::connpool=NULL;
int setnonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

//将内核事件表注册读事件，ET模式，选择开启EPOLLONESHOT
void addfd(int epollfd, int fd, bool one_shot)
{
    epoll_event event;
    event.data.fd = fd;

    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
    if (one_shot)
    event.events |= EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

//从内核时间表删除描述符
void removefd(int epollfd, int fd)
{
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
    close(fd);
}

//将事件重置为EPOLLONESHOT
void modfd(int epollfd, int fd, int ev)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);

}

int tcpconn::m_user_count = 0;
int tcpconn::m_epollfd = -1;

//关闭连接，关闭一个连接，客户总量减一
void tcpconn::close_conn(bool real_close)
{
    if (real_close && (m_sockfd != -1))
    {
        printf("close %d\n", m_sockfd);
        removefd(m_epollfd, m_sockfd);
        m_sockfd = -1;
        m_user_count--;
    }
}

//初始化连接,外部调用初始化套接字地址
void tcpconn::init(int sockfd, const sockaddr_in &addr)
{
    m_sockfd = sockfd;
    m_address = addr;
    int reuse=1;
    setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
    addfd(m_epollfd, sockfd, true);
    m_user_count++;

    init();
}

//初始化新接受的连接
//check_state默认为分析请求行状态
void tcpconn::init()
{
    
    bytes_to_send=0;
    bytes_have_send=0;
    res_have_send = 0;
    m_upload=0;
    m_login=false;
    m_signup=false;
    m_upload=false;
    m_trans=false;
    memset(m_read_buf, '\0', READ_BUFFER_SIZE);
    memset(m_write_buf, '\0', WRITE_BUFFER_SIZE);
    memset(m_real_file, '\0', FILENAME_LEN);
}
void tcpconn::tcpread()
{
    int length=0;
    if(!m_upload)  //不是在上传文件
    {
        memset(m_read_buf, '\0', READ_BUFFER_SIZE);
        length=recv(m_sockfd, m_read_buf, READ_BUFFER_SIZE, 0);
      //  modfd(m_epollfd, m_sockfd, EPOLLOUT);
        process_read(length);
        memset(m_read_buf, '\0', READ_BUFFER_SIZE);
    modfd(m_epollfd, m_sockfd, EPOLLOUT);
    }
    else
    {
	
std::cout<<"ready to write into file"<<std::endl;
	if(!m_trans)//传输正文
	{
	char temp[6];
	memset(temp,'\0',6);
	while(recv(m_sockfd, temp, 5, 0)<0)continue;
	if(!strcmp(temp,"TRANS")) m_trans=true;
	std::cout<<temp<<" : m_trans : "<<m_trans<<std::endl;
	
        modfd(m_epollfd, m_sockfd, EPOLLIN);
	return;
	}
        while((length=recv(m_sockfd, m_read_buf, READ_BUFFER_SIZE, 0))>0)
        {
std::cout<<"write into file "<<length<<std::endl;
           write(m_upload_fd, m_read_buf, length); //将读到的内容全部写入文件描述符
            memset(m_read_buf, '\0', READ_BUFFER_SIZE);
        }
	std::cout<<length<<std::endl;
    modfd(m_epollfd, m_sockfd, EPOLLIN);
    }
}
void tcpconn::process_read(int length)
{
    for (int i = 0; i < length;i++)
    {
        if(m_read_buf[i]==' ')
        {    
        memcpy(m_type, m_read_buf, i);
        memcpy(m_data, m_read_buf + i + 1, length - i-1);
        break;
        }
    }
	printf("%s\n",m_type);
    if(strcmp(m_type,"RELOAD")==0)
        deal_reload();
    if(strcmp(m_type,"LOGIN")==0)
        deal_login();
    if(strcmp(m_type,"SIGNUP")==0)
        deal_signup();
    if(strcmp(m_type,"UPLOAD")==0)
        deal_upload();
    if(strcmp(m_type,"DOWNLOAD")==0)
        deal_download();
}
void tcpconn::deal_reload()
{
printf("deal reload\n");
    //先从连接池取一个连接
    MYSQL *mysql = NULL;
    connectionRAII mysqlcon(&mysql, connpool);
    //ti添加字符集防止乱码
    mysql_set_character_set(mysql,"utf8");

    //在resources表中检索name，address数据，浏览器端输入
    mysql_query(mysql, "SELECT title,author,detail,filename FROM resources");
   
    //从表中检索完整的结果集
    MYSQL_RES *result = mysql_store_result(mysql);

    //返回结果集中的列数
    int num_fields = mysql_num_fields(result);

    //返回所有字段结构的数组
    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    m_resources.clear(); //清空容器
    //从结果集中获取下一行，存入vector中
    while (MYSQL_ROW row = mysql_fetch_row(result))
 {
    string temp0(row[0]);
    string temp1(row[1]);
    string temp2(row[2]);
    string temp3(row[3]);
 //获得文件大小
    strcpy(m_real_file,doc_root);
    int len=strlen(m_real_file);
    strncpy(m_real_file+len,temp3.c_str(),len);
    std::cout<<m_real_file<<std::endl;
    if (stat(m_real_file, &m_file_stat) < 0)
	{
	std::cout<<m_real_file<<" donot exist"<<std::endl;
	continue;}
    string size=std::to_string(m_file_stat.st_size);
    string str = temp0 + '&' + temp1 + '&' + temp2 + '&' + temp3+'&'+size;
    std::cout<<str<<std::endl;
    m_resources.push_back(str);
    }
}

void tcpconn::deal_login()
{
     char user[16],passwd[16]; 
     map<string,string> users;
     int i=0;
	while(m_data[i]!='&'&&m_data[i]!='\0')
	i++;
	if(m_data[i]=='\0')return;
	memcpy(user, m_data, i);
        memcpy(passwd, m_data + i + 1, strlen(m_data) - i-1);
     
std::cout<<user<<" : "<<passwd<<std::endl;
    //先从连接池中取一个连接
    MYSQL *mysql = NULL;
    connectionRAII mysqlcon(&mysql, connpool);
    //在userlab表中检索user，passwd数据
    mysql_query(mysql, "SELECT user,passwd FROM userlab");

    //从表中检索完整的结果集
    MYSQL_RES *result = mysql_store_result(mysql);

    //返回结果集中的列数
    int num_fields = mysql_num_fields(result);

    //返回所有字段结构的数组
    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    //从结果集中获取下一行，将对应的用户名和密码，存入map中
    while (MYSQL_ROW row = mysql_fetch_row(result))
    {
        string temp1(row[0]);
        string temp2(row[1]);
        users[temp1] = temp2;
    }
	if (users.find(user) != users.end() && users[user] == passwd)
	m_login=true;
}

void tcpconn::deal_signup()
{
    char name[64],email[32],user[16],passwd[16];
    set<string> userset;
    int i=0,head=0;
	while(m_data[i]!='&')
	i++;
	memcpy(name,m_data,i);
	head=++i;
	
	while(m_data[i]!='&')
	i++;
	memcpy(email,m_data+head,i-head);
	head=++i;

	while(m_data[i]!='&')
	i++;
	memcpy(user,m_data+head,i-head);
	head=++i;

	while(m_data[i]!='\0')
	++i;
	memcpy(passwd,m_data+head,i-head);
	std::cout<<name<<" : "<<email<<" : "<<user<<" : "<<passwd<<std::endl;

    //先从连接池中取一个连接
    MYSQL *mysql = NULL;
    connectionRAII mysqlcon(&mysql, connpool);
    //ti添加字符集防止乱码
    mysql_set_character_set(mysql,"utf8");
    //在userlab表中检索user，passwd数据
    mysql_query(mysql, "SELECT user FROM userlab");

    //从表中检索完整的结果集
    MYSQL_RES *result = mysql_store_result(mysql);

    //返回结果集中的列数
    int num_fields = mysql_num_fields(result);

    //返回所有字段结构的数组
    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    //从结果集中获取下一行，将对应的用户名和密码，存入map中
    while (MYSQL_ROW row = mysql_fetch_row(result))
    {
        string temp(row[0]);
        userset.insert(temp);
     }
//若不存在，则写入数据库
	if(userset.find(user)==userset.end())
	{
	 m_signup=true;
	 char *sql_insert = (char *)malloc(sizeof(char) * 200);
         strcpy(sql_insert, "INSERT INTO userlab(name,email,user,passwd) VALUES(");
         strcat(sql_insert, "'");
         strcat(sql_insert, name);
         strcat(sql_insert, "', '");
         strcat(sql_insert, email);
         strcat(sql_insert, "', '");
         strcat(sql_insert, user);
         strcat(sql_insert, "', '");
         strcat(sql_insert, passwd);
         strcat(sql_insert, "')");
         mysql_query(mysql, sql_insert);
	}
}
void tcpconn::deal_upload()
{
        
    int head=0,i=0,len=0;
    set<string> fileset;
    char title[256],author[256],detail[256],filename[256];
    while(m_data[i]!='&')
	i++;
	memcpy(title,m_data+head,i-head);
	head=++i;

    while(m_data[i]!='&')
	i++;
	memcpy(author,m_data+head,i-head);
	head=++i;
    while(m_data[i]!='&')
	i++;
	memcpy(detail,m_data+head,i-head);
	head=++i;
    while(m_data[i]!='\0')
	i++;
	memcpy(filename,m_data+head,i-head);
std::cout<<m_data<<std::endl;
std::cout<<title<<" : "<<author<<" : "<<detail<<" : "<<filename<<std::endl;
    strcpy(m_real_file,doc_root);
    len=strlen(m_real_file);
    strncpy(m_real_file+len,filename,strlen(filename));
    std::cout<<m_real_file<<std::endl;
    


    MYSQL *mysql = NULL;
    connectionRAII mysqlcon(&mysql, connpool);
    //ti添加字符集防止乱码
    mysql_set_character_set(mysql,"utf8");
    //在userlab表中检索user，passwd数据
    mysql_query(mysql, "SELECT filename FROM resources");

    //从表中检索完整的结果集
    MYSQL_RES *result = mysql_store_result(mysql);

    //返回结果集中的列数
    int num_fields = mysql_num_fields(result);

    //返回所有字段结构的数组
    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    //从结果集中获取下一行，将对应的用户名和密码，存入map中
    while (MYSQL_ROW row = mysql_fetch_row(result))
    {
        string temp(row[0]);
        fileset.insert(temp);
	std::cout<<temp<<std::endl;
     }
//若不存在，则写入数据库
	if(fileset.find(filename)==fileset.end())
	{
 	 m_upload = true;
	 m_upload_fd=open(m_real_file,O_WRONLY|O_CREAT|O_TRUNC);//创建文件

	 char *sql_insert = (char *)malloc(sizeof(char) * 200);
         strcpy(sql_insert, "INSERT INTO resources(title,author,detail,filename) VALUES(");
         strcat(sql_insert, "'");
         strcat(sql_insert, title);
         strcat(sql_insert, "', '");
         strcat(sql_insert, author);
         strcat(sql_insert, "', '");
         strcat(sql_insert, detail);
         strcat(sql_insert, "', '");
         strcat(sql_insert, filename);
         strcat(sql_insert, "')");
         mysql_query(mysql, sql_insert);
	}
}
 
void tcpconn::deal_download()
{  
    int len=0;
    strcpy(m_real_file,doc_root);
    len=strlen(m_real_file);
    strncpy(m_real_file+len,m_data,len);
	std::cout<<m_real_file<<std::endl;
    if (stat(m_real_file, &m_file_stat) < 0)
    {
	std::cout<<"file didnot exist"<<std::endl;
	return ;
     }
    int fd = open(m_real_file, O_RDONLY);
    m_file_address = (char *)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);
	bytes_to_send = m_file_stat.st_size;
} 

void tcpconn::send_reload()
{

    while(res_have_send<m_resources.size())
    {
        string tmp = m_resources[res_have_send++]+'\n';
        write(m_sockfd, tmp.c_str(), tmp.size());
    }

}
void tcpconn::send_login()
{
    if(m_login)
	write(m_sockfd,"true",5);
    else
	write(m_sockfd,"false",6);	
}

void tcpconn::send_signup()
{
    if(m_signup)
	write(m_sockfd,"true",5);
    else
	write(m_sockfd,"false",6);
}
void tcpconn::send_upload()
{
    if(m_upload)
    {
	write(m_sockfd,"true",5);
        modfd(m_epollfd, m_sockfd, EPOLLIN);
	std::cout<<"send true for upload"<<std::endl;
    }
    else 
    {
        write(m_sockfd,"false",6);
    }

}
void tcpconn::send_download()
{
     int temp=0;
     while(bytes_have_send<bytes_to_send)
     {
      if((temp = write(m_sockfd,m_file_address+bytes_have_send,2048))<0)
	{
	if(errno == EAGAIN)
	{
	  modfd(m_epollfd,m_sockfd,EPOLLOUT);
	}
	else
	{
	printf("传输出错\n");
        munmap(m_file_address, m_file_stat.st_size);
	 close_conn();
	}
	return;
	}
      bytes_have_send+=temp;
	std::cout<<"download..."<<bytes_have_send<<std::endl;
     }
    if (m_file_address)
    {
	printf("传输完成\n");
        munmap(m_file_address, m_file_stat.st_size);
        m_file_address = NULL;
	sleep(1);//延时关闭连接
	close_conn();
    }
}
void tcpconn::tcpwrite()
{
    if(strcmp(m_type,"RELOAD")==0)
        send_reload();
    if(strcmp(m_type,"LOGIN")==0)
        send_login();
    if(strcmp(m_type,"SIGNUP")==0)
        send_signup();
    if(strcmp(m_type,"UPLOAD")==0)
        send_upload();
    if(strcmp(m_type,"DOWNLOAD")==0)
        send_download();
       
}

void tcpconn::process()
{
    tcpread();
	printf("hreeaffe\n");
}
